#pragma once

#include <cppunit/config/SourcePrefix.h>
#include <iostream>
#include <string>
#include <algorithm>
#include "TestNodeTable.h"
#include "Utilities.h"

void NodeTableTest::setUp(){

	/*****************Node*******************/
	TreeNode* node1 = new TreeNode(AST_ASSIGN);
	validNodeInput.push_back(node1);
	TreeNode* node2 = new TreeNode(AST_STMT);
	validNodeInput.push_back(node2);
	TreeNode* node3 = new TreeNode(AST_PROCEDURE);
	validNodeInput.push_back(node3);
	TreeNode* node4 = new TreeNode(AST_WHILE);
	validNodeInput.push_back(node4);
	TreeNode* node5 = new TreeNode(AST_CALL);
	validNodeInput.push_back(node5);
	TreeNode* node6 = new TreeNode(AST_CONST);
	validNodeInput.push_back(node6);
	TreeNode* node7 = new TreeNode(AST_PROCEDURE);
	validNodeInput.push_back(node7);
	TreeNode* node8 = new TreeNode(AST_IF);
	validNodeInput.push_back(node8);

	TreeNode* node100 = NULL;
	invalidNodeInput.push_back(node100);
	TreeNode* node101 = new TreeNode();
	invalidNodeInput.push_back(node101);
	TreeNode* node102 = new TreeNode(AST_INVALID_TYPE);
	invalidNodeInput.push_back(node102);

	TreeNode* node200 = NULL;
	nonExistNodeInput.push_back(node200);
	TreeNode* node201 = new TreeNode(AST_CONST);
	nonExistNodeInput.push_back(node201);

	/******************Line*******************/
	validLineInput.push_back(1);
	validLineInput.push_back(2);
	validLineInput.push_back(3);
	validLineInput.push_back(5);
	validLineInput.push_back(6);
	validLineInput.push_back(7);
	validLineInput.push_back(8);
	validLineInput.push_back(9);

	invalidLineInput.push_back(0);

	nonExistLineInput.push_back(4);

	nodeTb = NodeTable();
}

void NodeTableTest::tearDown(){
	validNodeInput.clear();
	invalidNodeInput.clear();
	nonExistNodeInput.clear();

	validLineInput.clear();
	invalidLineInput.clear();
	nonExistLineInput.clear();
}

// Registers the fixture into the 'registry'
CPPUNIT_TEST_SUITE_REGISTRATION( NodeTableTest );

void NodeTableTest::testInsert(Type::List::Line lineList, std::vector<TreeNode*> nodeList,
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		TreeNode* tmpNode;
		unsigned int t, testNumber;
		bool tmpTF;

		if(lineList.size() > nodeList.size()){
			testNumber = lineList.size();
		}else{
			testNumber = nodeList.size();
		}

		for(t =0 ; t < testNumber; t++){
			tmpLine = lineList.at(Utilities::getIndexBoundedBySize(t, lineList.size()));
			tmpNode = nodeList.at(Utilities::getIndexBoundedBySize(t, nodeList.size()));

			tmpTF = nodeTb.insertNode(tmpLine, tmpNode);
			
			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg, CPPUNIT_ASSERT(tmpTF));
		}
}

void NodeTableTest::testGetLine(std::vector<TreeNode*> nodeList, Type::List::Line lineList, 
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine, bufLine;
		TreeNode* tmpNode;
		unsigned int t;
		bool tmpTF;

		for(t = 0; t < nodeList.size(); t++){
			tmpNode = nodeList.at(t);
			tmpLine = nodeTb.getLineByNode(tmpNode);

			bufLine = lineList.at(Utilities::getIndexBoundedBySize(t, lineList.size()));
			tmpTF = bufLine == tmpLine;
			
			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg,	CPPUNIT_ASSERT(tmpTF));
		}
}

void NodeTableTest::testGetNode(Type::List::Line lineList, std::vector<TreeNode*> nodeList, 
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		TreeNode* tmpNode = NULL;
		TreeNode* bufNode = NULL;
		unsigned int t;
		bool tmpTF;

		for(t = 0; t < lineList.size(); t++){
			tmpLine = lineList.at(t);
			tmpNode = nodeTb.getNodeByLine(tmpLine);

			bufNode = nodeList.at(Utilities::getIndexBoundedBySize(t, nodeList.size()));
			tmpTF = bufNode == tmpNode;

			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(failMsg,	CPPUNIT_ASSERT(tmpTF));
		}
}

void NodeTableTest::testIsExists(Type::List::Line lineList, std::vector<TreeNode*> nodeList, 
	bool pass, std::string failMsg){
		Type::Index::Line tmpLine;
		TreeNode* tmpNode;
		unsigned int t;
		bool tmpTF;

		for(t =0 ; t < lineList.size(); t++){
			tmpLine = lineList.at(t);
			tmpTF = nodeTb.isLineExistInNode(tmpLine);

			if(!pass){
				tmpTF = !tmpTF;
			}

			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(
				failMsg + " (line existence)" ,CPPUNIT_ASSERT(tmpTF));
		}

		for(t =0 ; t < nodeList.size(); t++){
			tmpNode = nodeList.at(t);
			tmpTF = nodeTb.isNodeExistInLine(tmpNode);

			if(!pass){
				tmpTF = !tmpTF;
			}
			CPPUNIT_ASSERT_ASSERTION_PASS_MESSAGE(
				failMsg + " (node existence)" ,CPPUNIT_ASSERT(tmpTF));
		}
}

// method to test the insertion of line, node
void NodeTableTest::testInsert(){
	std::cout<<std::endl<<"NodeTable Insert()"<<std::endl;

	// verify insertion of invalid line, valid node will always return false;
	msg = "Failed at insertion of invalid line, valid node";
	testInsert(invalidLineInput, validNodeInput, false, msg);

	// verify insertion of invalid node, valid line will always return false;
	msg = "Failed at insertion of valid line, invalid node";
	testInsert(validLineInput, invalidNodeInput, false, msg);

	// verify insertion of valid line, node will always return true;
	msg = "Failed at insertion of valid line, node";
	testInsert(validLineInput, validNodeInput, true, msg);

	// verify insertion of repeating valid line, node will always return false;
	msg = "Failed at insertion of repeating valid line, node";
	testInsert(validLineInput, validNodeInput, false, msg);
}

// method to test the retrieval of line, node
void NodeTableTest::testGet(){
	std::cout<<std::endl<<"NodeTable Get()"<<std::endl;

	testInsert();

	// verify line retrival of valid node will always correct;
	msg = "Failed at getting line of valid node";
	testGetLine(validNodeInput, validLineInput, true, msg);

	// verify line retrival of invalid node will always return false;
	msg = "Failed at getting line of invalid node";
	testGetLine(invalidNodeInput, validLineInput, false, msg);

	// verify node retrival of valid line will always correct;
	msg = "Failed at getting node of valid line";
	testGetNode(validLineInput, validNodeInput, true, msg);

	// verify node retrival of invalid line will always return false;
	msg = "Failed at getting node of valid line";
	testGetNode(invalidLineInput, validNodeInput, false, msg);
}

// method to test the existence of line, node
void NodeTableTest::testIsExists(){
	std::cout<<std::endl<<"NodeTable IsExist()"<<std::endl;

	testInsert();

	// verify existence of inserted valid line, node
	msg = "Failed checking existence of inserted line, node";
	testIsExists(validLineInput, validNodeInput, true, msg);

	// verify existence of invalid line, node
	msg = "Failed checking existence of invalid line, node";
	testIsExists(invalidLineInput, invalidNodeInput, false, msg);

	// verify existence of not inserted line, node
	msg = "Failed checking existence of not inserted line, node";
	testIsExists(nonExistLineInput, nonExistNodeInput, false, msg);
}